package com.k8.common.compiler.generator;

import com.k8.common.extension.Adaptive;
import com.k8.common.factroy.loader.ExtensionAccessor;
import com.k8.rpc.url.URL;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
public class AdaptiveClassCodeGenerator {

    private static final String CLASS_NAME_INVOCATION = "com.k8.rpc.invocation.Invocation";

    // 代码模板：包声明
    private static final String CODE_PACKAGE = "package %s;\n";
    // 代码模板：导入语句
    private static final String CODE_IMPORTS = "import %s;\n";
    // 代码模板：类声明
    private static final String CODE_CLASS_DECLARATION = "public class %s$Adaptive implements %s {\n";
    // 代码模板：方法声明
    private static final String CODE_METHOD_DECLARATION = "public %s %s(%s) %s {\n%s}\n";
    // 代码模板：方法参数
    private static final String CODE_METHOD_ARGUMENT = "%s arg%d";
    // 代码模板：方法抛出异常
    private static final String CODE_METHOD_THROWS = "throws %s";
    // 代码模板：不支持的方法
    private static final String CODE_UNSUPPORTED =
            "throw new UnsupportedOperationException(\"The method %s of interface %s is not adaptive method!\");\n";
    // 代码模板：URL参数空检查
    private static final String CODE_URL_NULL_CHECK =
            "if (arg%d == null) throw new IllegalArgumentException(\"url == null\");\n%s url = arg%d;\n";
    // 代码模板：扩展名称赋值
    private static final String CODE_EXT_NAME_ASSIGNMENT = "String extName = %s;\n";
    // 代码模板：扩展名称空检查
    private static final String CODE_EXT_NAME_NULL_CHECK = "if(extName == null) "
            + "throw new IllegalStateException(\"Failed to get extension (%s) name from url (\" + url.toString() + \") use keys(%s)\");\n";
    // 代码模板：Invocation参数空检查
    private static final String CODE_INVOCATION_ARGUMENT_NULL_CHECK =
            "if (arg%d == null) throw new IllegalArgumentException(\"invocation == null\"); "
                    + "String methodName = arg%d.getMethodName();\n";
    // 代码模板：扩展访问器赋值（无占位符，修正格式问题）
    private static final String CODE_ACCESSOR_ASSIGNMENT = "ExtensionAccessor accessor = url.getExtensionAccessor();\n";
    // 代码模板：扩展实例赋值（明确扩展类型）
    private static final String CODE_EXTENSION_ASSIGNMENT =
            "%s extension = (%<s)accessor.getExtensionLoader(%s.class).getExtension(extName);\n";
    // 代码模板：方法调用参数
    private static final String CODE_EXTENSION_METHOD_INVOKE_ARGUMENT = "arg%d";

    // 目标接口类型
    private final Class<?> type;
    // 默认扩展名称
    private final String defaultExtName;

    public AdaptiveClassCodeGenerator(Class<?> type, String defaultExtName) {
        this.type = type;
        this.defaultExtName = defaultExtName;
    }

    /**
     * 检查接口是否有至少一个被@Adaptive注解的方法（仅检查自身声明的方法，排除Object继承的方法）
     */
    private boolean hasAdaptiveMethod() {
        return Arrays.stream(type.getDeclaredMethods()) // 只获取接口自身声明的方法
                .filter(m -> !m.getDeclaringClass().equals(Object.class)) // 排除Object的方法
                .anyMatch(m -> m.isAnnotationPresent(Adaptive.class));
    }

    /**
     * 生成自适应类代码
     */
    public String generate() {
        return generate(false);
    }

    /**
     * 生成自适应类代码（支持排序方法）
     *
     * @param sort 是否对方法排序
     */
    public String generate(boolean sort) {
        if (!hasAdaptiveMethod()) {
            throw new IllegalStateException("No adaptive method exists on extension " + type.getName()
                    + ", refuse to create the adaptive class!");
        }

        StringBuilder code = new StringBuilder();
        code.append(generatePackageInfo());
        code.append(generateImports());
        code.append(generateClassDeclaration());

        Method[] methods = type.getDeclaredMethods(); // 只处理接口自身声明的方法
        if (sort) {
            Arrays.sort(methods, Comparator.comparing(Method::toString));
        }
        for (Method method : methods) {
            code.append(generateMethod(method));
        }
        code.append('}');

        if (log.isDebugEnabled()) {
            log.debug("Generated adaptive class code:\n{}", code);
        }
        return code.toString();
    }

    /**
     * 生成包信息
     */
    private String generatePackageInfo() {
        return String.format(CODE_PACKAGE, type.getPackage().getName());
    }

    /**
     * 生成导入语句
     */
    private String generateImports() {
        StringBuilder builder = new StringBuilder();
        // 导入必要的类（根据实际使用动态添加）
        builder.append(String.format(CODE_IMPORTS, ExtensionAccessor.class.getName()));
        builder.append(String.format(CODE_IMPORTS, URL.class.getName()));
        builder.append(String.format(CODE_IMPORTS, type.getName()));
        return builder.toString();
    }

    /**
     * 生成类声明
     */
    private String generateClassDeclaration() {
        return String.format(CODE_CLASS_DECLARATION, type.getSimpleName(), type.getCanonicalName());
    }

    /**
     * 生成不支持的方法（未被@Adaptive注解的方法）
     */
    private String generateUnsupported(Method method) {
        return String.format(CODE_UNSUPPORTED, method, type.getName());
    }

    /**
     * 获取URL类型参数的索引
     */
    private int getUrlTypeIndex(Method method) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            if (parameterTypes[i].equals(URL.class)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 生成方法代码
     */
    private String generateMethod(Method method) {
        String returnType = method.getReturnType().getCanonicalName();
        String methodName = method.getName();
        String methodContent = generateMethodContent(method);
        String args = generateMethodArguments(method);
        String throwsClause = generateMethodThrows(method);
        return String.format(CODE_METHOD_DECLARATION, returnType, methodName, args, throwsClause, methodContent);
    }

    /**
     * 生成方法参数列表
     */
    private String generateMethodArguments(Method method) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        return IntStream.range(0, parameterTypes.length)
                .mapToObj(i -> String.format(CODE_METHOD_ARGUMENT, parameterTypes[i].getCanonicalName(), i))
                .collect(Collectors.joining(", "));
    }

    /**
     * 生成方法抛出的异常列表
     */
    private String generateMethodThrows(Method method) {
        Class<?>[] exceptionTypes = method.getExceptionTypes();
        if (exceptionTypes.length == 0) {
            return "";
        }
        String exceptions = Arrays.stream(exceptionTypes)
                .map(Class::getCanonicalName)
                .collect(Collectors.joining(", "));
        return String.format(CODE_METHOD_THROWS, exceptions);
    }

    /**
     * 生成URL参数的空检查代码
     */
    private String generateUrlNullCheck(int index) {
        return String.format(CODE_URL_NULL_CHECK, index, URL.class.getName(), index);
    }

    /**
     * 生成方法体内容
     */
    private String generateMethodContent(Method method) {
        Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
        if (adaptiveAnnotation == null) {
            return generateUnsupported(method);
        }

        StringBuilder code = new StringBuilder(512);
        int urlTypeIndex = getUrlTypeIndex(method);

        // 处理URL参数（直接或间接获取）
        if (urlTypeIndex != -1) {
            code.append(generateUrlNullCheck(urlTypeIndex)); // 直接使用URL参数
        } else {
            code.append(generateUrlAssignmentIndirectly(method)); // 间接从其他参数获取URL
        }

        // 处理Invocation参数（获取方法名用于动态选择扩展）
        boolean hasInvocation = hasInvocationArgument(method);
        code.append(generateInvocationArgumentNullCheck(method));

        // 生成扩展名称（extName）的赋值逻辑
        String[] adaptiveKeys = getMethodAdaptiveValue(adaptiveAnnotation);
        code.append(generateExtNameAssignment(adaptiveKeys, hasInvocation));

        // 检查扩展名称是否为空
        code.append(generateExtNameNullCheck(adaptiveKeys));

        // 获取扩展访问器和扩展实例
        code.append(generateAccessorAssignment());
        code.append(generateExtensionAssignment());

        // 生成方法调用和返回语句
        code.append(generateReturnAndInvocation(method));

        return code.toString();
    }

    /**
     * 生成扩展名称的空检查代码
     */
    private String generateExtNameNullCheck(String[] adaptiveKeys) {
        return String.format(CODE_EXT_NAME_NULL_CHECK, type.getName(), Arrays.toString(adaptiveKeys));
    }

    /**
     * 生成扩展实例的赋值代码
     */
    private String generateExtensionAssignment() {
        return String.format(CODE_EXTENSION_ASSIGNMENT, type.getName(), type.getName());
    }

    /**
     * 生成扩展访问器的赋值代码（修正格式参数问题）
     */
    private String generateAccessorAssignment() {
        return CODE_ACCESSOR_ASSIGNMENT; // 无占位符，直接返回模板
    }

    /**
     * 生成扩展名称（extName）的赋值逻辑
     * 优先从URL参数获取，其次使用默认扩展名，支持Invocation方法级参数
     */
    private String generateExtNameAssignment(String[] adaptiveKeys, boolean hasInvocation) {
        if (adaptiveKeys == null || adaptiveKeys.length == 0) {
            throw new IllegalArgumentException("Adaptive keys cannot be empty");
        }

        // 构建extName的获取逻辑（从后往前拼接，优先级：前序key > 后序key > 默认值）
        String getNameCode = null;
        for (int i = adaptiveKeys.length - 1; i >= 0; i--) {
            String key = adaptiveKeys[i];
            if (i == adaptiveKeys.length - 1) {
                // 最后一个key，带默认值
                if (defaultExtName != null) {
                    getNameCode = buildGetParamCode(key, defaultExtName, hasInvocation);
                } else {
                    getNameCode = buildGetParamCode(key, null, hasInvocation);
                }
            } else {
                // 非最后一个key，以前序结果为默认值
                getNameCode = buildGetParamCode(key, getNameCode, hasInvocation);
            }
        }

        return String.format(CODE_EXT_NAME_ASSIGNMENT, getNameCode);
    }

    /**
     * 构建获取URL参数的代码片段（支持方法级参数）
     */
    private String buildGetParamCode(String key, String defaultValue, boolean hasInvocation) {
        if ("protocol".equals(key)) {
            // 特殊处理protocol：优先用url.getProtocol()
            return defaultValue != null
                    ? String.format("(url.getProtocol() == null ? %s : url.getProtocol())", wrapQuotation(defaultValue))
                    : "url.getProtocol()";
        }

        // 普通参数：支持方法级参数（通过Invocation获取methodName）
        if (hasInvocation) {
            return defaultValue != null
                    ? String.format("url.getMethodParameter(methodName, \"%s\", %s)", key, wrapQuotation(defaultValue))
                    : String.format("url.getMethodParameter(methodName, \"%s\")", key);
        } else {
            return defaultValue != null
                    ? String.format("url.getParameter(\"%s\", %s)", key, wrapQuotation(defaultValue))
                    : String.format("url.getParameter(\"%s\")", key);
        }
    }

    /**
     * 为字符串添加引号（用于生成代码中的字符串常量）
     */
    private String wrapQuotation(String value) {
        return "\"" + value + "\"";
    }

    /**
     * 生成方法调用和返回语句
     */
    private String generateReturnAndInvocation(Method method) {
        String returnKeyword = method.getReturnType().equals(void.class) ? "" : "return ";
        String args = IntStream.range(0, method.getParameterCount())
                .mapToObj(i -> String.format(CODE_EXTENSION_METHOD_INVOKE_ARGUMENT, i))
                .collect(Collectors.joining(", "));
        return returnKeyword + String.format("extension.%s(%s);\n", method.getName(), args);
    }

    /**
     * 检查方法是否包含Invocation类型的参数
     */
    private boolean hasInvocationArgument(Method method) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        return Arrays.stream(parameterTypes)
                .anyMatch(paramType -> CLASS_NAME_INVOCATION.equals(paramType.getCanonicalName()));
    }

    /**
     * 生成Invocation参数的空检查代码
     */
    private String generateInvocationArgumentNullCheck(Method method) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        return IntStream.range(0, parameterTypes.length)
                .filter(i -> CLASS_NAME_INVOCATION.equals(parameterTypes[i].getCanonicalName()))
                .mapToObj(i -> String.format(CODE_INVOCATION_ARGUMENT_NULL_CHECK, i, i))
                .findFirst()
                .orElse("");
    }

    /**
     * 获取@Adaptive注解的value值，若为空则生成默认key（驼峰转小写+点分隔）
     */
    private String[] getMethodAdaptiveValue(Adaptive adaptiveAnnotation) {
        String[] value = adaptiveAnnotation.value();
        if (value.length > 0) {
            return value;
        }
        // 生成默认key：接口名驼峰转小写+点分隔（如RpcProtocol → rpc.protocol）
        String defaultKey = camelToDotSeparated(type.getSimpleName());
        return new String[]{defaultKey};
    }

    /**
     * 驼峰命名转点分隔命名（如RpcProtocol → rpc.protocol）
     */
    private String camelToDotSeparated(String camelCase) {
        if (camelCase == null || camelCase.isEmpty()) {
            return "";
        }
        StringBuilder result = new StringBuilder();
        result.append(Character.toLowerCase(camelCase.charAt(0)));
        for (int i = 1; i < camelCase.length(); i++) {
            char c = camelCase.charAt(i);
            if (Character.isUpperCase(c)) {
                result.append('.').append(Character.toLowerCase(c));
            } else {
                result.append(c);
            }
        }
        return result.toString();
    }

    /**
     * 从方法参数中间接获取URL（参数包含返回URL的getter方法）
     */
    private String generateUrlAssignmentIndirectly(Method method) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        Map<String, Integer> urlGetterMethods = new HashMap<>();

        // 收集所有返回URL的getter方法（public、非静态、无参数）
        for (int i = 0; i < parameterTypes.length; i++) {
            Class<?> paramType = parameterTypes[i];
            for (Method getter : paramType.getMethods()) {
                if (isValidUrlGetter(getter)) {
                    urlGetterMethods.put(getter.getName(), i);
                }
            }
        }

        if (urlGetterMethods.isEmpty()) {
            throw new IllegalStateException("No URL parameter or URL getter found in method "
                    + method.getName() + " of interface " + type.getName());
        }

        // 优先选择getUrl()方法，否则报错（避免随机选择）
        if (urlGetterMethods.containsKey("getUrl")) {
            int index = urlGetterMethods.get("getUrl");
            return generateGetterNullCheck(index, parameterTypes[index], "getUrl");
        } else {
            throw new IllegalStateException("No getUrl() method found in parameters of method "
                    + method.getName() + " (required for indirect URL access)");
        }
    }

    /**
     * 检查是否为有效的URL getter方法（public、非静态、无参数、返回URL）
     */
    private boolean isValidUrlGetter(Method method) {
        return Modifier.isPublic(method.getModifiers())
                && !Modifier.isStatic(method.getModifiers())
                && method.getParameterCount() == 0
                && method.getReturnType() == URL.class;
    }

    /**
     * 生成间接获取URL的空检查代码（参数非空 + getter返回值非空）
     */
    private String generateGetterNullCheck(int paramIndex, Class<?> paramType, String getterMethod) {
        StringBuilder code = new StringBuilder();
        // 检查参数本身非空
        code.append(String.format("if (arg%d == null) throw new IllegalArgumentException(\"%s argument cannot be null\");\n",
                paramIndex, paramType.getSimpleName()));
        // 检查getter返回值非空
        code.append(String.format("if (arg%d.%s() == null) throw new IllegalArgumentException(\"%s.%s() returns null\");\n",
                paramIndex, getterMethod, paramType.getSimpleName(), getterMethod));
        // 赋值URL变量
        code.append(String.format("%s url = arg%d.%s();\n", URL.class.getName(), paramIndex, getterMethod));
        return code.toString();
    }
}